#include <stdio.h>
#include <stdlib.h>

#include "config.h"
#include "reg.h"
#include "debug.h"
#include "parser.h"

static int reg_set = 0;

char *reg_name[REG_SIZE] =
{
  "eax",
  "edx",
  "ebx",
  "ecx",
  "edi",
  "esi",
  "ebp",
  "esp",
};

static struct reg_set reg_stack[REG_STATCK_DEPTH];
static struct reg_status reg_status_stack[REG_STATCK_DEPTH];

int reg_stack_top = 0;
int reg_status_stack_top = 0;

static inline int test_reg_bit(int reg_set, int nr)
{
  return(1 << nr) & reg_set;
}

static inline void set_reg_bit(int *reg_set, int nr)
{
  *reg_set = (*reg_set) | (1 << nr);
}

static inline void clear_reg_bit(int *reg_set, int nr)
{
  *reg_set = (*reg_set) & (~(1 << nr));
}

int get_reg()
{
  int i = 0;
  int ret_reg = -1;

  for (i = 0; i < COMMON_REG_NUM * (32 / COMMON_REG_NUM); i++)
  {
    if (!test_reg_bit(reg_set, i))
    {
      __DEBUG_MSG("find register %s is free\n", reg_name[i % COMMON_REG_NUM]);
      set_reg_bit(&reg_set, i);

      if (i < COMMON_REG_NUM)
      {
        reg_status_stack[reg_status_stack_top].reg = i;
        reg_status_stack[reg_status_stack_top].status = 0;/*not overflow*/
        reg_status_stack_top++;

        return i;
      }
      else/*register overflow*/
      {
        reg_stack[reg_stack_top].offset = i;
        ret_reg = i % COMMON_REG_NUM;
        reg_stack[reg_stack_top].reg = ret_reg;
        __DEBUG_MSG("register:%s overflow, so we need save it\n", reg_name[ret_reg]);
        output(";save\n");
        output("push %s;\n", reg_name[ret_reg]);
        output(";end save\n");

        reg_stack_top ++;
        if (reg_stack_top >= REG_STATCK_DEPTH)
        {
          __ERROR_MSG("reg stack is full\n");
          exit(-1);
        }

        reg_status_stack[reg_status_stack_top].reg = ret_reg;
        reg_status_stack[reg_status_stack_top].status = 1;/*overflow*/
        reg_status_stack_top++;

        return ret_reg;
      }
    }
  }

  __ERROR_MSG("can't find usable register\n");
  return -1;
}

int get_specified_reg(int reg)
{
  int i = 0;

  int ret_reg = -1;

  if (reg >= COMMON_REG_NUM || reg < 0)
  {
    __ERROR_MSG("wrong parameter\n");
    return -1;
  }

  for (i = reg; i < COMMON_REG_NUM * (32 / COMMON_REG_NUM); i += COMMON_REG_NUM)
  {
    if (!test_reg_bit(reg_set, i))
    {
      __DEBUG_MSG("find register %s is free\n", reg_name[i % COMMON_REG_NUM]);
      set_reg_bit(&reg_set, i);

      if (i < COMMON_REG_NUM)
      {
        reg_status_stack[reg_status_stack_top].reg = i;
        reg_status_stack[reg_status_stack_top].status = 0;/*overflow*/
        reg_status_stack_top++;

        return 0;
      }
      else/*register overflow*/
      {
        reg_stack[reg_stack_top].offset = i;
        ret_reg = i % COMMON_REG_NUM;
        reg_stack[reg_stack_top].reg = ret_reg;

        __DEBUG_MSG("register:%s overflow, so we need save it\n", reg_name[ret_reg]);
        output(";save\n");
        output("push %s;\n", reg_name[ret_reg]);
        output(";end save\n");

        reg_stack_top ++;
        if (reg_stack_top >= REG_STATCK_DEPTH)
        {
          __ERROR_MSG("reg stack is full\n");
          exit(-1);
        }

        reg_status_stack[reg_status_stack_top].reg = ret_reg;
        reg_status_stack[reg_status_stack_top].status = 1;/*overflow*/
        reg_status_stack_top++;

        return 0;
      }
    }
  }

  __ERROR_MSG("can't get specified register:%s\n", reg_name[reg]);
  return -1;
}

/**
 * get reg except EAX, EDX
 */
int get_div_reg()
{
  int i = 0;
  int ret_reg = -1;

  for (i = 2; i < COMMON_REG_NUM * (32 / COMMON_REG_NUM);)
  {
    if (!test_reg_bit(reg_set, i))
    {
      __DEBUG_MSG("find register %s is free\n", reg_name[i % COMMON_REG_NUM]);
      set_reg_bit(&reg_set, i);

      if (i < COMMON_REG_NUM)
      {
        reg_status_stack[reg_status_stack_top].reg = i;
        reg_status_stack[reg_status_stack_top].status = 0;/*overflow*/
        reg_status_stack_top++;

        return i;
      }
      else/*register overflow*/
      {
        reg_stack[reg_stack_top].offset = i;
        ret_reg = i % COMMON_REG_NUM;
        reg_stack[reg_stack_top].reg = ret_reg;
        __DEBUG_MSG("register:%s overflow, so we need save it\n", reg_name[ret_reg]);
        output(";save\n");
        output("push %s;\n", reg_name[ret_reg]);
        output(";end save\n");

        reg_stack_top ++;
        if (reg_stack_top >= REG_STATCK_DEPTH)
        {
          __ERROR_MSG("reg stack is full\n");
          exit(-1);
        }

        reg_status_stack[reg_status_stack_top].reg = ret_reg;
        reg_status_stack[reg_status_stack_top].status = 1;/*overflow*/
        reg_status_stack_top++;

        return ret_reg;
      }
    }

    i ++;

    if ((i % COMMON_REG_NUM) == 0)/*skip eax edx*/
    {
      i += 2;
    }
  }

  __ERROR_MSG("can't find usable register\n");
  return -1;
}

int free_reg(int reg)
{
  int ret = 0;

  if (reg >= COMMON_REG_NUM * (32 / COMMON_REG_NUM)
      || reg < 0)
  {
    __ERROR_MSG("wrong parameter:%d\n", reg);
    return -1;
  }


  if (reg_status_stack_top <= 0)
  {
    __ERROR_MSG("reg status stack is empty, no reg has allocated\n");
    return -1;
  }

  reg_status_stack_top --;
  if (reg_status_stack[reg_status_stack_top].reg != reg)
  {
    __ERROR_MSG("reg allocated and freed sequence is wrong!\n");
    return -1;
  }

  if (reg_status_stack[reg_status_stack_top].status)/*register is on the stack*/
  {
    reg_stack_top --;
    if (reg_stack[reg_stack_top].reg != reg)
    {
      ret = -1;
      __ERROR_MSG("the reg which to freed is not on the top of stack\n");
    }
    else
    {
      if (test_reg_bit(reg_set, reg_stack[reg_stack_top].offset))
      {
        __DEBUG_MSG("free reg %s\n", reg_name[reg]);
        clear_reg_bit(&reg_set, reg_stack[reg_stack_top].offset);
        output(";restore\n");
        output("pop %s;\n", reg_name[reg]);
        output(";end restore\n");
      }
      else
      {
        ret = -1;
        __ERROR_MSG("reg %s was already free\n", reg_name[reg]);
      }
    }
  }
  else
  {
    if (test_reg_bit(reg_set, reg))
    {
      __DEBUG_MSG("free reg %s\n", reg_name[reg]);
      clear_reg_bit(&reg_set, reg);
    }
    else
    {
      ret = -1;
      __ERROR_MSG("reg %s was already free\n", reg_name[reg]);
    }
  }

  return ret;
}

void clear_reg_bit_set()
{
  reg_set = 0;
  return;
}

